package erpc

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"time"
)

type piecestoreClient struct {
	conn net.Conn
}
type uploadClient struct {
	stream Stream
}
type downloadClient struct {
	stream Stream
}

func (p *uploadClient) Send(request *Chunk) error {

	data, err := json.Marshal(request)
	if err != nil {
		return err
	}

	buf := Int2bytes(len(data))
	buf2 := append(buf, data...)
	nSend, err := p.stream.Write(buf2)
	if err != nil {
		return err
	}
	if nSend != len(buf2) {
		return errors.New("send less than request")
	}
	return nil
}
func (p *uploadClient) CloseAndRecv() (*UploadResult, error) {
	s := spliter{
		stream: p.stream,
	}

	size, err := s.readLen()
	if err != nil {
		return nil, err
	}
	data, err := s.readN(size)
	if err != nil {
		return nil, err
	}
	var result = &UploadResult{}
	err = json.Unmarshal(data, result)
	if err != nil {
		return nil, err
	}
	return result, nil
}

func (p *downloadClient) Recv() (*Chunk, error) {
	s := spliter{
		stream: p.stream,
	}

	size, err := s.readLen()
	if err != nil {
		return nil, err
	}
	data, err := s.readN(size)
	if err != nil {
		return nil, err
	}
	var result = &Chunk{}
	err = json.Unmarshal(data, result)
	if err != nil {
		return nil, err
	}
	return result, nil
}
func (p *downloadClient) CloseAndRecv() error {
	buf := make([]byte, 5)
	_, err := p.stream.Read(buf)
	return err
}

func (p *piecestoreClient) Upload(ctx context.Context,
	pieceId string, expireTime time.Time, rateBytePerSecond int64) (UploadClient, error) {
	upload := UploadRequest{
		PieceId:    []byte(pieceId),
		ExpireTime: int(expireTime.Unix()),
	}
	data, err := json.Marshal(upload)
	if err != nil {
		return nil, err
	}
	msg := Msg{
		Data:    data,
		MsgType: MsgUpload,
	}
	data2, err := msg.Marshal()
	if err != nil {
		return nil, err
	}
	if deadLine, ok := ctx.Deadline(); ok {
		fmt.Println("SetWriteDeadline", deadLine)
		p.conn.SetWriteDeadline(deadLine)
	}
	_, err = p.conn.Write(data2)
	if err != nil {
		return nil, err
	}
	limit := NewLimitStream(p.conn, rateBytePerSecond)
	return &uploadClient{
		stream: limit,
	}, nil
}
func (p *piecestoreClient) Download(ctx context.Context, pieceId string,
	rateBytePerSecond int64) (DownloadClient, error) {
	request := DownloadRequst{
		PieceId:    []byte(pieceId),
		SpeedLimit: rateBytePerSecond,
	}
	data, err := json.Marshal(request)
	if err != nil {
		return nil, err
	}
	msg := Msg{
		Data:    data,
		MsgType: MsgDownload,
	}
	data2, err := msg.Marshal()
	if err != nil {
		return nil, err
	}
	if deadLine, ok := ctx.Deadline(); ok {
		p.conn.SetWriteDeadline(deadLine)
	}
	_, err = p.conn.Write(data2)
	if err != nil {
		return nil, err
	}
	return &downloadClient{stream: p.conn}, nil
}
func (p *piecestoreClient) Close() string {
	var h = p.conn.LocalAddr().String()
	_, port, _ := net.SplitHostPort(h)
	p.conn.Close()
	return port
}
func NewDRPCPiecestoreClient(conn net.Conn) RpcPieceStoreClient {
	return &piecestoreClient{
		conn: conn,
	}
}
